﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using cc2530.Lib.contiki;

namespace cc2530.Lib.device
{



   
    /// <summary>
    /// 转发 类，用于处理串口到tap ,tap 到串口的过程
    /// </summary>
    class Tranformation
    {
        //static const int VTC   = 14; //数据链路层头部大小 6 + 6 + 2

        /// <summary>
        ///  用于pc 发送ns 包处理  ns 的 option 数据变换，变换原则:pc的48位mac ---> EUI64标准的 link layer address
        /// </summary>
        /// <param name="packet"></param>
        /// <returns></returns>
        static IPv6Packet _NS_MAC802_To_EUI64(IPv6Packet packet)
        {
            
            IPv6ICMPOption option = packet.Ipv6NSheader.options;
            option.type = IPv6ICMPOptionType.LinkSourceAddress;
            byte[] d = option.data;

            byte[] no = new byte[2 + 8];
            no[0] = (byte)IPv6ICMPOptionType.LinkSourceAddress;
            no[1] = 2;//以8字节为单位
            //生成8字节 interface id
            Array.Copy(IPv6Address.ToEUI48(d, 2).GetAddressBytes(), 8, no, 2, 8);

            option.data = no;

            //重设option data
            IPv6NeighborSolicitationHeader ns = packet.Ipv6NSheader;
            ns.options = option;
            packet.Ipv6NSheader = ns;

            //计算icmp checksum
            IPv6ICMPHeader icmph = packet.ToIPv6ICMPHeader();
            icmph.checksum = packet.toIPv6ICMPPseudoHeader().ToCheckSum();
            packet.Ipv6icmpHeader = icmph;

            return packet;
        }


        /// <summary>
        /// 用于contiki节点返回NA 包处理，原则,EUI64位地址转换成 802标准的 48位mac 地址
        /// </summary>
        /// <param name="packet"></param>
        /// <returns></returns>
        static IPv6Packet _NA_EUI64_To_MAC802(IPv6Packet packet) {

            //更改 linklayer address   EUI64------>802 mac(48)
            IPv6ICMPOption opt = packet.Ipv6NAheader.options;
            opt.type = IPv6ICMPOptionType.LinkDestationAddress;
            byte[] _d = opt.data;
            byte[] _no = new byte[8];
            _no[0] = (byte)IPv6ICMPOptionType.LinkDestationAddress;
            _no[1] = 1;
            opt.data = _no;
            Array.Copy(IPv6Address.FromEUI64(_d, 2), 0, _no, 2, 6);
            IPv6NeighborAdvertisementHeader na = packet.Ipv6NAheader;
            na.options = opt;
            packet.Ipv6NAheader = na;
            IPv6ICMPHeader _icmph = packet.ToIPv6ICMPHeader();
            _icmph.checksum = packet.toIPv6ICMPPseudoHeader().ToCheckSum();
            packet.Ipv6icmpHeader = _icmph;
            return packet;
        }

        /// <summary>
        /// 通用变换
        /// </summary>
        /// <param name="packet"></param>
        /// <returns></returns>
        static IPv6Packet _tranformation(IPv6Packet packet)
        {
            if (packet.hardType != HardType.IPv6) return null;
            try {

                    //目前只对ICMPV6包处理
                    if(packet.IPv6header.NextHeader == IPv6NextHeaderType.IPv6_ICMP)
                        switch (packet.Ipv6icmpHeader.type) { 
                        
                            //NS 包
                            case ICMPv6Type.Neighbor_Solicitation_NDP:
                                packet = _NS_MAC802_To_EUI64(packet);
                                break;
                            //NA包
                            case ICMPv6Type.Neighbor_Advertisement_NDP:
                                packet = _NA_EUI64_To_MAC802(packet);
                                break;

                            // ping 的 request
                            case ICMPv6Type.Echo_Request:
                                break;
                            
                            // ping 的 replay
                            case ICMPv6Type.Echo_Reply:

                                break;
                        
                           ///路由请求
                            case ICMPv6Type.Router_Solicitation_NDP:
                                break;

                            ///路由通告
                            case ICMPv6Type.Router_Advertisement_NDP:

                                break;
                            ///重定向
                            case ICMPv6Type.Redirect_Message_NDP:
                                break;

                            default:
                                break;
                        }
            }
            catch(Exception e) {
                System.Console.WriteLine(e.ToString());
            }    
            return packet;
        }
        

        /// <summary>
        /// tap 到 serialport 的处理过程
        /// </summary>
        /// <param name="args">入参，tap中接收到的数据包</param>
        /// <returns>出参，返回要发送的数据包</returns>
        public static TransationBase Tap2Serial (TransationBase args){   
            //ipv6 包
            IPv6Packet packet = new IPv6Packet(args.data, args.datalen);
            packet = _tranformation(packet);
            if (packet == null) return null;
            //返回 IP 部分数据
            TransationBase rages = new TransationBase();
            rages.data = packet.IPPayload;
            rages.datalen = rages.data.Length;
            return rages;
        }

        /// <summary>
        /// 串口到tap 的处理过程，当然，要加入 link layer 的 mac 头啦
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        public static TransationBase Serial2Tap(TransationBase args,byte[] SrcMac,byte[] DestMac){

            //添加源mac 与 目标 mac
            byte[] lldata = new byte[args.datalen + 14];
            Array.Copy(DestMac, 0, lldata, 0, 6);
            Array.Copy(SrcMac, 0, lldata, 6, 6);
            Array.Copy(args.data, 0, lldata, 14, args.datalen);
            lldata[12] = (byte)((UInt16)HardType.IPv6>>8);
            lldata[13] = (byte)((UInt16)HardType.IPv6 & 0x00FF);

            ///ipv6 数据包
            if ((byte)args.data[0] >> 4 == 0x06 && args.datalen>40) { 
            

                IPv6Packet packet = new IPv6Packet(lldata, lldata.Length);

                packet = _tranformation(packet);
                if (packet == null) return null;

                args.data = packet.PacketData;
                args.datalen = args.data.Length;

                return args;
            
            }

            //其他包  --加入头部直接返回
            args.data = lldata;
            args.datalen = args.data.Length;
            return args;
                  
        }

    }
}
